Skip to content

Conversation

@mfbz
Copy link
Contributor

@mfbz mfbz commented Dec 10, 2025

Added react-native-sdk package, similar to react-sdk but for react-native applications. It fully supports all the same hooks available in react-sdk, plus the connect and profile components. It leverages fcl-react-native for managing blockchain interactions and it's compatible to both react-native and expo applications.

Here's some screenshots:

Description Image
Splash
Home
Connect
Connected
Profile

@chasefleming chasefleming requested a review from Copilot December 15, 2025 22:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new @onflow/react-native-sdk package that brings Flow blockchain integration to React Native and Expo applications. The package provides the same hooks as react-sdk plus native mobile components (Connect and Profile) for wallet management. Additionally, it includes improvements to the WalletConnect integration in fcl-react-native for better mobile deep linking support and disconnect handling.

Key changes:

  • New React Native SDK package with hooks, components, and providers
  • Enhanced WalletConnect service with deep link transformation for mobile wallets
  • Refactored disconnect logic to properly clean up WalletConnect sessions
  • Updated demo with mobile starter template promotion

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react-native-sdk/package.json Package manifest for the new React Native SDK
packages/react-native-sdk/src/index.ts Main entry point re-exporting hooks, components, and types
packages/react-native-sdk/src/provider/FlowProvider.tsx Provider component that sets up Flow client and context
packages/react-native-sdk/src/provider/GlobalTransactionProvider.tsx Provider for tracking global transaction state
packages/react-native-sdk/src/components/Connect.tsx Wallet connection button with profile modal
packages/react-native-sdk/src/components/Profile.tsx User profile component displaying balance and wallet info
packages/react-native-sdk/src/icons/*.tsx Icon components for UI elements
packages/fcl-react-native/src/walletconnect/service.ts Enhanced deep link transformation for mobile wallets
packages/fcl-react-native/src/walletconnect/client.ts New disconnect utility function
packages/fcl-react-native/src/fcl-react-native.ts Refactored unauthenticate to use shared disconnect logic
packages/fcl-react-native/src/client.ts Updated createFlowClient with disconnect integration
packages/demo/src/components/starter-banner.tsx Added mobile starter template card

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +21
} & (
| {vaultIdentifier: string; erc20Address?: never}
| {vaultIdentifier?: never; erc20Address: string}
)
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TokenConfig type doesn't allow both vaultIdentifier and erc20Address to be specified simultaneously, which prevents supporting tokens that exist on both Cadence and EVM. Consider allowing both properties to be optional without mutual exclusion to support cross-VM tokens.

Suggested change
} & (
| {vaultIdentifier: string; erc20Address?: never}
| {vaultIdentifier?: never; erc20Address: string}
)
vaultIdentifier?: string
erc20Address?: string
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct as it is because you must specify one or the other like in react-sdk

// Transform uid format (e.g., "frw#authz") to deeplink URL (e.g., "frw://authz")
// Service UIDs use # as separator but mobile deeplinks need ://
if (uid && uid.includes("#") && !uid.includes("://")) {
return uid.replace("#", "://")
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string replacement logic assumes UIDs always have exactly one '#' character. If a UID contains multiple '#' characters, only the first will be replaced, potentially creating an invalid deep link. Consider using a more robust transformation that handles edge cases or validates the UID format.

Suggested change
return uid.replace("#", "://")
const [scheme, ...rest] = uid.split("#");
return scheme + "://" + rest.join("#");

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed because in case it has multiple "#" it's a wrong uid and it should error

@@ -0,0 +1,59 @@
# @onflow/react-native-sdk

A React Native library that provides hooks for interacting with the Flow blockchain. It helps you authenticate users, run Cadence scripts and transactions, listen to events, and manage network configuration directly from your components. Fully compatible with Expo.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a line here about it being comparable to the React SDK and add a link. We could maybe add a link to the hooks section of the playground as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

</FlowProvider>
```

## 🪝 Available Hooks
Copy link
Member

@chasefleming chasefleming Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put a link to a full list in case it gets out of date. And also put a note like here is a look at some of the hooks available just to account for the list becoming not up to date.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

| {vaultIdentifier?: never; erc20Address: string}
)

export interface ConnectProps {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these the same as the connect component? Should it be shared? How do we make sure these don't get out of sync. Or maybe it doesn't matter if they do since they are separate components?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep for the components i'd keep them separated because they could have different implementations, like in this case where there is a reduced interface.

)
}

const styles = StyleSheet.create({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the component themeable like React SDK? I don't think we need to bother with that, but on the docs/playground we should make a note that theming is not available in the React Native SDK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not themeable right now for sake of simplicity (i saw we were doing some changes also on react-sdk so i avoided doing that). Yep, i omitted the theme section in the Docs (PR).

minHeight: 48,
},
disconnectedButton: {
backgroundColor: "#0F172A",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make a file to share the colors across components? Same with any other styling constants.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, added!

if (!chainId) return []

const getFlowTokenAddress = () => {
if (chainId === "emulator" || chainId === "local")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have constants for these

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you that constants would be cleaner, but useFlowChainId return value is a string and probably it's ok and flexible enough with the forking changes we are doing where the chainId can be mainnet-fork, etc

const address = getFlowTokenAddress().replace("0x", "")
return [
{
symbol: "FLOW",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constant for this as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should be constant? It's implemented exactly as the Profile component in the react-sdk and it's already getting the address from contracts constants. If it's the name or symbol hopefully it won't ever change 😂

: CONTRACT_ADDRESSES.mainnet.FlowToken
}

const address = getFlowTokenAddress().replace("0x", "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use the withPrefix method we have

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep updated with correct method also on react-sdk profile component for consistency


const displayAddress = useMemo(() => {
if (!user?.addr) return ""
return `${user.addr.slice(0, 6)}...${user.addr.slice(-4)}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this to a helper? I feel like we must do this often

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes make sense. I've created a util function "truncateAddress" in react-core and updated usage in both react-sdk and react-native-sdk using it

},
}

export function FlowProvider({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How similar is this to the provider we already have? Can't be shared?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The providers (react-sdk and react-native-sdk) share interfaces and contexts from react-core (FlowConfig, FlowConfigContext, FlowClientContext, QueryClient setup) but the implementation differs due to platform requirements:

  • react-sdk: Uses fcl and web-specific ConnectModal
  • react-native-sdk: Uses fcl-react-native and mobile-specific ConnectModalProvider

The core logic (config context, client creation, query client setup) is identical, the difference is the fcl import and modal provider which are platform specific, so for this their implementation is different.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you might be able to just wrap this at a platform-level @mfbz

i.e.

function Foo() {
    const flowClient = //platform-specific impl
    return (<>
        <FlowProvider flowClient={flowClient}>
    </)
}

(or similar)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the fact was that the FlowProvider for react-native-sdk still needed some platform-specific implementation, like the ConnectModalProvider to show the connect modal and some other minor things that were needed in order to make it have the same usage as the react-sdk provider. For this reason, even if i created a FlowProviderCore wrapper it would have inside only a few things and it would still need a platform specific implementation so i kept them with the same interface but separated.

import {QueryClient} from "@tanstack/react-query"
import {FlowQueryClientContext} from "@onflow/react-core"

export function FlowQueryClientProvider({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case they are basically the same. I have moved it to react-core so that it's reused for both react-sdk and react-native-sdk, thanks for the catch!

@mfbz mfbz merged commit 706e082 into master Dec 16, 2025
8 of 9 checks passed
@mfbz mfbz deleted the mfbz/react-native-sdk branch December 16, 2025 21:00
@github-actions github-actions bot mentioned this pull request Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants